package com.mlethe.library.calendarview;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;

import androidx.annotation.NonNull;

import java.util.List;

/**
 * 月视图基础控件,可自由继承实现
 *
 * @Author Mlethe
 * @Date 2023/1/29
 */
public abstract class BaseMonthView extends View implements View.OnClickListener, View.OnLongClickListener {

    CalendarViewDelegate mDelegate;

    /**
     * 当前月份日期的笔
     */
    protected Paint mCurMonthTextPaint = new Paint();

    /**
     * 其它月份日期颜色
     */
    protected Paint mOtherMonthTextPaint = new Paint();

    /**
     * 标记的日期背景颜色画笔
     */
    protected Paint mSchemePaint = new Paint();

    /**
     * 被选择的日期背景色
     */
    protected Paint mSelectedPaint = new Paint();

    /**
     * 标记的文本画笔
     */
    protected Paint mSchemeTextPaint = new Paint();

    /**
     * 选中的文本画笔
     */
    protected Paint mSelectTextPaint = new Paint();

    /**
     * 当前日期文本颜色画笔
     */
    protected Paint mCurDayTextPaint = new Paint();

    /**
     * 日历项
     */
    protected List<Calendar> mItems;

    /**
     * 每一项的高度
     */
    protected int mItemHeight;

    /**
     * 每一项的宽度
     */
    protected int mItemWidth;

    /**
     * Text的基线
     */
    protected float mTextBaseLine;

    /**
     * 触屏偏差
     */
    protected final int mTouchSlop;

    /**
     * 点击的x、y坐标
     */
    protected float mX, mY;

    /**
     * 是否点击
     */
    boolean isClick = true;

    /**
     * 当前日历卡年份
     */
    protected int mYear;

    /**
     * 当前日历卡月份
     */
    protected int mMonth;

    /**
     * 月起始日
     */
    protected int mMonthStartDay = 1;

    /**
     * 日历的行数
     */
    protected int mLineCount;

    /**
     * 日历高度
     */
    protected int mHeight;

    /**
     * 当前点击项
     */
    protected int mCurrentItem = -1;

    /**
     * 字体大小
     */
    static final int TEXT_SIZE = 14;

    /**
     * 日历点击监听事件
     */
    protected OnCalendarClickListener mOnCalendarClickListener;

    public BaseMonthView(Context context) {
        super(context);
        ViewConfiguration configuration = ViewConfiguration.get(context);
        mTouchSlop = configuration.getScaledTouchSlop();
    }

    /**
     * 初始化所有UI配置
     *
     * @param delegate delegate
     */
    final void setup(CalendarViewDelegate delegate) {
        this.mDelegate = delegate;
        this.mMonthStartDay = delegate.getMonthStartDay();
        initPaint(getContext());
        updateStyle();
    }

    /**
     * 初始化配置
     *
     * @param context context
     */
    private void initPaint(Context context) {
        mCurMonthTextPaint.setAntiAlias(true);
        mCurMonthTextPaint.setTextAlign(Paint.Align.CENTER);
        mCurMonthTextPaint.setColor(0xFF111111);
        mCurMonthTextPaint.setFakeBoldText(true);
        mCurMonthTextPaint.setTextSize(CalendarUtil.dipToPx(context, TEXT_SIZE));

        mOtherMonthTextPaint.setAntiAlias(true);
        mOtherMonthTextPaint.setTextAlign(Paint.Align.CENTER);
        mOtherMonthTextPaint.setColor(0xFFe1e1e1);
        mOtherMonthTextPaint.setFakeBoldText(true);
        mOtherMonthTextPaint.setTextSize(CalendarUtil.dipToPx(context, TEXT_SIZE));

        mSchemeTextPaint.setAntiAlias(true);
        mSchemeTextPaint.setStyle(Paint.Style.FILL);
        mSchemeTextPaint.setTextAlign(Paint.Align.CENTER);
        mSchemeTextPaint.setColor(0xffed5353);
        mSchemeTextPaint.setFakeBoldText(true);
        mSchemeTextPaint.setTextSize(CalendarUtil.dipToPx(context, TEXT_SIZE));

        mSelectTextPaint.setAntiAlias(true);
        mSelectTextPaint.setStyle(Paint.Style.FILL);
        mSelectTextPaint.setTextAlign(Paint.Align.CENTER);
        mSelectTextPaint.setColor(0xffed5353);
        mSelectTextPaint.setFakeBoldText(true);
        mSelectTextPaint.setTextSize(CalendarUtil.dipToPx(context, TEXT_SIZE));

        mSchemePaint.setAntiAlias(true);
        mSchemePaint.setStyle(Paint.Style.FILL);
        mSchemePaint.setStrokeWidth(2);
        mSchemePaint.setColor(0xffefefef);

        mCurDayTextPaint.setAntiAlias(true);
        mCurDayTextPaint.setTextAlign(Paint.Align.CENTER);
        mCurDayTextPaint.setColor(Color.RED);
        mCurDayTextPaint.setFakeBoldText(true);
        mCurDayTextPaint.setTextSize(CalendarUtil.dipToPx(context, TEXT_SIZE));

        mSelectedPaint.setAntiAlias(true);
        mSelectedPaint.setStyle(Paint.Style.FILL);
        mSelectedPaint.setStrokeWidth(2);

        setOnClickListener(this);
        setOnLongClickListener(this);
    }

    /**
     * 更新样式
     */
    final void updateStyle() {
        if (mDelegate == null) {
            return;
        }
        this.mCurDayTextPaint.setColor(mDelegate.getCurDayTextColor());
        this.mCurMonthTextPaint.setColor(mDelegate.getCurrentMonthTextColor());
        this.mOtherMonthTextPaint.setColor(mDelegate.getOtherMonthTextColor());
        this.mSelectTextPaint.setColor(mDelegate.getSelectedTextColor());
        this.mSchemePaint.setColor(mDelegate.getSchemeThemeColor());
        this.mSchemeTextPaint.setColor(mDelegate.getSchemeTextColor());
        this.mCurMonthTextPaint.setTextSize(mDelegate.getDayTextSize());
        this.mOtherMonthTextPaint.setTextSize(mDelegate.getDayTextSize());
        this.mCurDayTextPaint.setTextSize(mDelegate.getDayTextSize());
        this.mSchemeTextPaint.setTextSize(mDelegate.getDayTextSize());
        this.mSelectTextPaint.setTextSize(mDelegate.getDayTextSize());

        this.mSelectedPaint.setStyle(Paint.Style.FILL);
        this.mSelectedPaint.setColor(mDelegate.getSelectedThemeColor());
    }

    /**
     * 初始化日期
     *
     * @param year  年
     * @param month 月
     */
    final void initMonthWithDate(int year, int month) {
        this.mYear = year;
        this.mMonth = month;
        updateItemHeight();
        mItems = CalendarUtil.initCalendarForMonthView(mYear, mMonth, mMonthStartDay, mDelegate);
        updateSelectCalendar(mYear, mMonth, mMonthStartDay);
        addSchemesFromMap();
        onCreate();
    }

    /**
     * 获取选中的日期
     *
     * @param year          年
     * @param month         月
     * @param monthStartDay 月起始日
     */
    private void updateSelectCalendar(int year, int month, int monthStartDay) {
        Calendar selectCalendar = CalendarUtil.getSelectCalendar(year, month, monthStartDay, mDelegate.getCurrentDay(), mDelegate.getDefaultCalendarSelectMode(), mDelegate.getSelectedCalendar());
        setSelectedCalendar(selectCalendar);
    }

    /**
     * 移除事件
     */
    final void removeSchemes() {
        for (Calendar a : mItems) {
            a.setScheme("");
            a.setSchemeColor(0);
            a.setSchemes(null);
        }
    }

    /**
     * 设置日历点击事件
     *
     * @param listener 点击事件监听
     */
    final void setOnCalendarClickListener(OnCalendarClickListener listener) {
        this.mOnCalendarClickListener = listener;
    }

    /**
     * 添加事件标记，来自Map
     */
    final void addSchemesFromMap() {
        if (mDelegate.mSchemeDatesMap == null || mDelegate.mSchemeDatesMap.isEmpty()) {
            return;
        }
        for (Calendar a : mItems) {
            if (mDelegate.mSchemeDatesMap.containsKey(a.toString())) {
                Calendar d = mDelegate.mSchemeDatesMap.get(a.toString());
                if (d == null) {
                    continue;
                }
                a.setScheme(d.getScheme());
                a.setSchemeColor(d.getSchemeColor());
                a.setSchemes(d.getSchemes());
            } else {
                a.setScheme("");
                a.setSchemeColor(0);
                a.setSchemes(null);
            }
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (mLineCount != 0) {
            heightMeasureSpec = MeasureSpec.makeMeasureSpec(mHeight, MeasureSpec.EXACTLY);
        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    /**
     * 获取点击选中的日期
     *
     * @return position
     */
    protected int getIndex() {
        if (mItemWidth <= 0 || mItemHeight <= 0) {
            return -1;
        }
        if (mX <= mDelegate.getCalendarPaddingLeft() || mX >= getWidth() - mDelegate.getCalendarPaddingRight()) {
            return -1;
        }
        int indexX = (int) (mX - mDelegate.getCalendarPaddingLeft()) / mItemWidth;
        if (indexX >= 7) {
            indexX = 6;
        }
        int indexY = (int) mY / mItemHeight;
        int position = indexY * 7 + indexX;// 选择项
        if (position >= 0 && position < mItems.size()) {
            return position;
        }
        return -1;
    }

    /**
     * 更新月起始日
     *
     * @param monthStartDay 月起始日
     */
    final void updateMonthStartDay(int monthStartDay) {
        if (mMonthStartDay == monthStartDay) return;
        int year = mYear;
        int month = mMonth;
        int oldMonthStartDay = mMonthStartDay;
        int currentDay = CalendarUtil.getDay();
        if (oldMonthStartDay > currentDay && monthStartDay <= currentDay) {
            month = month + 1;
            if (month >= 13) {
                month = 12;
                year = year + 1;
            }
        } else if (oldMonthStartDay <= currentDay && monthStartDay > currentDay) {
            month = month - 1;
            if (month <= 0) {
                month = 12;
                year = year - 1;
            }
        }
        this.mYear = year;
        this.mMonth = month;
        this.mMonthStartDay = monthStartDay;
        updateData();
    }

    /**
     * 更新选中日期
     */
    final void updateSelectCalendar() {
        updateSelectCalendar(mYear, mMonth, mMonthStartDay);
    }

    /**
     * 更新当前日期
     */
    void updateCurrentDate() {
        if (mItems == null) return;
        Calendar currentDay = mDelegate.getCurrentDay();
        for (Calendar a : mItems) {
            a.setCurrentDay(a.equals(currentDay));
        }
    }

    /**
     * 更新标记日期
     */
    void updateScheme() {
        if (mDelegate.mSchemeDatesMap == null || mDelegate.mSchemeDatesMap.size() <= 0) {//清空操作
            removeSchemes();
            return;
        }
        addSchemesFromMap();
    }

    /**
     * 更新Items数据
     */
    final void updateData() {
        mLineCount = CalendarUtil.getMonthLineCount(mYear, mMonth, mMonthStartDay, mDelegate.getWeekStart(), mDelegate.getMonthViewShowMode());
        mHeight = mLineCount * mItemHeight;
        mItems = CalendarUtil.initCalendarForMonthView(mYear, mMonth, mMonthStartDay, mDelegate);
        updateSelectCalendar(mYear, mMonth, mMonthStartDay);
        addSchemesFromMap();
    }

    /**
     * 更新item高度
     */
    final void updateItemHeight() {
        mItemHeight = mDelegate.getCalendarItemHeight();
        Paint.FontMetrics metrics = mCurMonthTextPaint.getFontMetrics();
        mTextBaseLine = mItemHeight / 2f - metrics.descent + (metrics.bottom - metrics.top) / 2;
        mLineCount = CalendarUtil.getMonthLineCount(mYear, mMonth, mMonthStartDay, mDelegate.getWeekStart(), mDelegate.getMonthViewShowMode());
        mHeight = mLineCount * mItemHeight;
    }

    /**
     * 记录已经选择的日期
     *
     * @param calendar calendar
     */
    int setSelectedCalendar(Calendar calendar) {
        mCurrentItem = -1;
        if (calendar == null) return -1;
        int size = mItems.size();
        for (int i = 0; i < size; i++) {
            Calendar cal = mItems.get(i);
            if (cal.equals(calendar) && cal.isCurrentMonth()) {
                mCurrentItem = i;
                return i;
            }
        }
        return -1;
    }

    /**
     * 获取选中的下标
     *
     * @param calendar calendar
     * @return 获取选中的下标
     */
    int getSelectedIndex(Calendar calendar) {
        return mItems.indexOf(calendar);
    }

    /**
     * 是否在日期范围内
     *
     * @param calendar calendar
     * @return 是否在日期范围内
     */
    protected final boolean isInRange(Calendar calendar) {
        return mDelegate != null && CalendarUtil.isCalendarInRange(calendar, mDelegate);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        try {
            if (event.getPointerCount() > 1)
                return false;
        } catch (Exception e) {
            e.printStackTrace();
        }
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mX = event.getX();
                mY = event.getY();
                isClick = true;
                break;
            case MotionEvent.ACTION_MOVE:
                if (isClick) {
                    float dy = event.getY() - mY;
                    float dx = event.getY() - mY;
                    isClick = Math.abs(dy) <= mTouchSlop && Math.abs(dx) <= mTouchSlop;
                }
                break;
            case MotionEvent.ACTION_UP:
                mX = event.getX();
                mY = event.getY();
                break;
        }
        return super.onTouchEvent(event);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        if (canvas == null) return;
        if (mLineCount == 0) return;
        mItemWidth = (getWidth() -
                mDelegate.getCalendarPaddingLeft() -
                mDelegate.getCalendarPaddingRight()) / 7;
        onPreviewHook();
        int d = 0;
        for (int i = 0; i < mLineCount; i++) {
            for (int j = 0; j < 7; j++) {
                Calendar calendar = mItems.get(d);
                if (mDelegate.getMonthViewShowMode() == CalendarViewDelegate.MODE_ONLY_CURRENT_MONTH) {
                    if (!calendar.isCurrentMonth()) {
                        ++d;
                        continue;
                    }
                }
                draw(canvas, calendar, i, j, d);
                ++d;
            }
        }
    }

    /**
     * 开始绘制
     *
     * @param canvas   canvas
     * @param calendar 对应日历
     * @param row      行
     * @param column   列
     * @param position 位置
     */
    protected void draw(@NonNull Canvas canvas, @NonNull Calendar calendar, int row, int column, int position) {
        // TODO: 2023/2/3
    }

    /**
     * 初始化完成后，这里做一些初始化的操作，只会调用一次
     */
    protected void onCreate() {
        // TODO: 2023/2/2
    }

    /**
     * 开始绘制前的钩子，这里做一些初始化的操作，每次绘制只调用一次，性能高效
     * 没有需要可忽略不实现
     * 例如：
     * 1、需要绘制圆形标记事件背景，可以在这里计算半径
     * 2、绘制矩形选中效果，也可以在这里计算矩形宽和高
     */
    protected void onPreviewHook() {
        // TODO: 2022/12/29
    }

    /**
     * 循环绘制开始的回调，不需要可忽略
     * 绘制每个日历项的循环，用来计算baseLine、圆心坐标等都可以在这里实现
     *
     * @param x 日历Card x起点坐标
     * @param y 日历Card y起点坐标
     */
    protected void onLoopStart(int x, int y) {
        // TODO: 2022/12/29
    }

    protected void onDestroy() {
    }

    interface OnCalendarClickListener {
        void onClick(Calendar calendar, int position);
    }
}
